﻿using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace JetBlack.Runtime.InteropServices
{
    /// <summary>
    /// Office application.
    /// </summary>
    public enum OfficeApp
    {
        /// <summary>
        /// Excel.
        /// </summary>
        Excel
    }

    /// <summary>
    /// The load behaviour of a COM excel addin.
    /// </summary>
    [Flags]
    public enum LoadBehaviour
    {
        /// <summary>
        /// Is not loaded.
        /// </summary>
        Disconnect = 0,

        /// <summary>
        /// Is loaded.
        /// </summary>
        Connected = 1,

        /// <summary>
        /// Load on application startup.
        /// </summary>
        Bootload = 2,

        /// <summary>
        /// Load when the application is running without a window.
        /// </summary>
        CommandLine = 4,

        /// <summary>
        /// Load only when requested by user.
        /// </summary>
        DemandLoad = 8,

        /// <summary>
        /// Load only once (on next startup).
        /// </summary>
        ConnectFirstTime = 16
    }

    /// <summary>
    /// Registry extension class.
    /// </summary>
    public static class RegistryExtensions
    {
        /// <summary>
        /// Register an automation addin.
        /// </summary>
        /// <param name="type">The type to register.</param>
        public static void RegisterAutomationAddin(this Type type)
        {
            var clsidKey = GetClsidKeyName(type);
            Registry.ClassesRoot.CreateSubKey(clsidKey + @"\Programmable");
            using (var key = Registry.ClassesRoot.OpenSubKey(clsidKey + @"\InprocServer32", true))
                key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll", RegistryValueKind.String);
        }

        /// <summary>
        /// Unregister an automation addin.
        /// </summary>
        /// <param name="type">The type to unregister.</param>
        public static void UnregisterAutomationAddin(this Type type)
        {
            Registry.ClassesRoot.DeleteSubKey(GetClsidKeyName(type) + @"\Programmable");
        }

        private static string GetClsidKeyName(Type type)
        {
            return string.Concat(@"CLSID\{", type.GUID.ToString().ToUpper(), "}");
        }

        /// <summary>
        /// Register a COM addin.
        /// </summary>
        /// <param name="type">The type to register.</param>
        /// <param name="officeApp">The office appplication for which this type is created.</param>
        /// <param name="friendlyName">The friendly name of the addin.</param>
        /// <param name="description">A description of the addin.</param>
        /// <param name="loadBehaviour">The behaviour of the addin when the application starts up.</param>
        /// <param name="isCommandLineSafe">If <c>true</c> the addin does not require a window.</param>
        public static void RegisterCOMAddin(this Type type, OfficeApp officeApp, string friendlyName, string description, LoadBehaviour loadBehaviour, bool isCommandLineSafe)
        {
            using (var officeBase = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office", true))
            {
                using (var excelBase = officeBase.CreateSubKey(officeApp.ToString()))
                {
                    using (var addinBase = excelBase.CreateSubKey("Addins"))
                    {
                        using (var key = addinBase.CreateSubKey(type.ProgId()))
                        {
                            key.SetValue("Description", description, RegistryValueKind.String);
                            key.SetValue("FriendlyName", friendlyName, RegistryValueKind.String);
                            key.SetValue("LoadBehavior", (int)loadBehaviour, RegistryValueKind.DWord);
                            key.SetValue("CommandLineSafe", isCommandLineSafe ? 1 : 0, RegistryValueKind.DWord);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Unregister a COM addin.
        /// </summary>
        /// <param name="type">The type to unregister.</param>
        /// <param name="officeApp">The office appplication for which this type is created.</param>
        public static void UnregisterCOMAddin(this Type type, OfficeApp officeApp)
        {
            Registry.CurrentUser.DeleteSubKey(@"Software\Microsoft\Office\" + officeApp.ToString() + @"\Addins\" + type.ProgId());
        }

        private static string ProgId(this Type type)
        {
            var progIdAttribute = (ProgIdAttribute)type.GetCustomAttributes(typeof(ProgIdAttribute), true).FirstOrDefault();
            return
                progIdAttribute == null
                ? type.FullName
                : progIdAttribute.Value;
        }
    }
}
